// ==UserScript==
// @name           Breconium-KoC
// @version        0.1.16 alpha
// @namespace      KSX
// @description    The Breconium connector for KoC
// @homepage       http://www.ksware.de/apps/Breconium
// @copyright      Copyright 2012 KSX - Non-commercial use only. No modifications permitted.
// @license        CC BY-NC-ND 3.0 http://creativecommons.org/licenses/by-nc-sa/3.0/

// @include        *kingdomsofcamelot.com/*main_src.php*
// @-include        *kingdomsofcamelot.com/*fbLoginButton.php*
// @-include        *kingdomsofcamelot.com/*standAlone.php*
// @-include        *kingdomsofcamelot.com/*platforms/kabam*
// @-include        *kingdomsofcamelot.com/*acceptToken_src.php*
// @-include        *kingdomsofcamelot.com/*helpFriend_src.php*
// @-include        *apps.facebook.com/kingdomsofcamelot/*
// @-include        *facebook.com/connect/uiserver.php*
// @-include        *facebook.com/*/serverfbml*
// @-include        *facebook.com/dialog/feed*
// @-include        *facebook.com/dialog/stream.publish*
// @-include        *facebook.com/dialog/apprequests*
// @-include        *kabam.com/*

// ==/UserScript==

var Version       = GM_getMetadata?GM_getMetadata("version"): '0.1.3 alpha';
var ScriptName    = GM_getMetadata?GM_getMetadata("name")   : "Breconium-KoC";

var ServerUri = "http://127.0.0.1:33333";
var Options = {
	Version: 1,
	EnabledErrorConsoleTrace: true
};

var SessionId = "0";
var LastMessageId = 0;
var CommandPollLoopCommandResp = null;

var JSON; if (!JSON) { JSON = {}; } (function () { function str(a, b) { var c, d, e, f, g = gap, h, i = b[a]; if (i && typeof i === "object" && typeof i.toJSON === "function") { i = i.toJSON(a); } if (typeof rep === "function") { i = rep.call(b, a, i) } switch (typeof i) { case "string": return quote(i); case "number": return isFinite(i) ? String(i) : "null"; case "boolean": case "null": return String(i); case "object": if (!i) { return "null" } gap += indent; h = []; if (Object.prototype.toString.apply(i) === "[object Array]") { f = i.length; for (c = 0; c < f; c += 1) { h[c] = str(c, i) || "null" } e = h.length === 0 ? "[]" : gap ? "[\n" + gap + h.join(",\n" + gap) + "\n" + g + "]" : "[" + h.join(",") + "]"; gap = g; return e } if (rep && typeof rep === "object") { f = rep.length; for (c = 0; c < f; c += 1) { if (typeof rep[c] === "string") { d = rep[c]; e = str(d, i); if (e) { h.push(quote(d) + (gap ? ": " : ":") + e) } } } } else { for (d in i) { if (Object.prototype.hasOwnProperty.call(i, d)) { e = str(d, i); if (e) { h.push(quote(d) + (gap ? ": " : ":") + e) } } } } e = h.length === 0 ? "{}" : gap ? "{\n" + gap + h.join(",\n" + gap) + "\n" + g + "}" : "{" + h.join(",") + "}"; gap = g; return e } } function quote(a) { escapable.lastIndex = 0; return escapable.test(a) ? '"' + a.replace(escapable, function (a) { var b = meta[a]; return typeof b === "string" ? b : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) }) + '"' : '"' + a + '"' } function f(a) { return a < 10 ? "0" + a : a } "use strict"; if (typeof Date.prototype.toJSON !== "function") { Date.prototype.toJSON = function (a) { return isFinite(this.valueOf()) ? this.getUTCFullYear() + "-" + f(this.getUTCMonth() + 1) + "-" + f(this.getUTCDate()) + "T" + f(this.getUTCHours()) + ":" + f(this.getUTCMinutes()) + ":" + f(this.getUTCSeconds()) + "Z" : null }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (a) { return this.valueOf() } } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r", '"': '\\"', "\\": "\\\\" }, rep; if (typeof JSON.stringify !== "function") { JSON.stringify = function (a, b, c) { var d; gap = ""; indent = ""; if (typeof c === "number") { for (d = 0; d < c; d += 1) { indent += " " } } else if (typeof c === "string") { indent = c } rep = b; if (b && typeof b !== "function" && (typeof b !== "object" || typeof b.length !== "number")) { throw new Error("JSON.stringify") } return str("", { "": a }) } } if (typeof JSON.parse !== "function") { JSON.parse = function (text, reviver) { function walk(a, b) { var c, d, e = a[b]; if (e && typeof e === "object") { for (c in e) { if (Object.prototype.hasOwnProperty.call(e, c)) { d = walk(e, c); if (d !== undefined) { e[c] = d } else { delete e[c] } } } } return reviver.call(a, b, e) } var j; text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) }) } if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) { j = eval("(" + text + ")"); return typeof reviver === "function" ? walk({ "": j }, "") : j } throw new SyntaxError("JSON.parse") } } })();

if (Options.EnabledErrorConsoleTrace) logit("ScriptStart" + "\r\t" + "location: " + window.location.href);
status("Start "+ScriptName+" "+ Version+" Connecting...");
InitConnection();

function InitConnection() {
	if (Options.EnabledErrorConsoleTrace) logit("InitConnection()");

	var initVer = "init v1";
	var init = {
		Client        : ScriptName,
		Version       : Version,
		Location      : window.location.href,
		ClientSpecific: {
			server    : "270",
			playerId  : "0815",
			playerName: "kux"
		}
	};

	var ret = GM_xmlhttpRequest({
		method: "POST", url: ServerUri + "/init",
		headers: { "Content-Type": "application/json" },
		data: JSON.stringify({ SID: SessionId, MID: ++LastMessageId, MT: "init", DT: initVer, DATA: init }),
		onload: function (response) {
			if (Options.EnabledErrorConsoleTrace) logit(response.responseText);
			InitSuccess(response);
		},
		onerror: function (response) {
			if (response.readyState == 4) {
				status("ERROR InitConnection failed. Server offline? Connecting...");
				if (Options.EnabledErrorConsoleTrace) logit("InitConnection failed. Server offline?");
				setTimeout(function () { InitConnection(); }, 5000);
			} else {
				var msg = "An error occurred."
					+ "\nlocation: " + window.location.href
					+ "\nresponseText: " + response.responseText
					+ "\nreadyState: " + response.readyState
					+ "\nresponseHeaders: " + response.responseHeaders
					+ "\nstatus: " + response.status
					+ "\nstatusText: " + response.statusText
					+ "\nfinalUrl: " + response.finalUrl;
				logit(msg);
				setTimeout(function () { InitConnection() }, 5000);
			}

		}
	});
}

function InitSuccess(response) {
	status("OK InitSuccess");
	if (Options.EnabledErrorConsoleTrace) logit("InitSuccess()");
	GM_log("response.responseJSON:" + response.responseJSON);
	SessionId = response.responseJSON.SID;

	StartCommandPoll();
}

function StartCommandPoll() {
	CommandPollLoopCommandResp = { SID: SessionId, MID: 0, MT: "COMMAND_POLL", CH:0, DT: "", DATA: {} };
	setTimeout(function () { CommandPollLoop(); }, 500);
}

function CommandPollLoop() {
	if (Options.EnabledErrorConsoleTrace) logit("CommandPollLoop()");

	var ret = GM_xmlhttpRequest({
		method: "POST", url: ServerUri + "/command",
		headers: { "Content-Type": "application/json" },
		data: JSON.stringify(CommandPollLoopCommandResp),
		onload: function(response){CommandPollSuccess(response);},
		onerror: function (response) {
			if (response.readyState == 4) {
				status("ERROR CommandPoll failed. Server offline? Connecting...");
			} else {
				var msg = "An error occurred."
					+ "\nlocation: " + window.location.href
					+ "\nresponseText: " + response.responseText
					+ "\nreadyState: " + response.readyState
					+ "\nresponseHeaders: " + response.responseHeaders
					+ "\nstatus: " + response.status
					+ "\nstatusText: " + response.statusText
					+ "\nfinalUrl: " + response.finalUrl;
				logit(msg);
			}
			setTimeout(function () { InitConnection(); }, 5000);
		}
	});
}

function CommandPollSuccess(response){
	if (Options.EnabledErrorConsoleTrace) logit("CommandPollSuccess"+"\n"+response.responseText);
	CommandPollLoopCommandResp = { SID: SessionId, MID: 0, MT: "COMMAND_POLL", CH:0, DT: "", DATA: {} };
	try{
		var message=new TE.Message(response.responseJSON);
		var commandReq=new TE.CommandReqData(message.DATA);
		var commandName = commandReq.Command;
		if (typeof (commandName) == "undefined") throw new Error("Command not specified!");
		switch (commandName) {
			case "nop"        : break;
			case "eval"       : Client_eval(message); break;
			case "ajaxrequest": Client_AjaxRequest(message); break;
			case "reload"     : Reload(); break;
			case "GM_log"     : GM_log(EvalArguments(message.DATA.Arguments).toString()); break;
			case "alert"      : alert("huhu from " + ScriptName); break;
			case "pushkocdata": BeginPushKocData(); break;
			default: logit("CommandPollLoop ERROR Unknown command '"+commandName+"'!");break;
		}
	} catch(ex){
		logit("CommandPollLoop ERROR "+ex.name+" "+ex.message);
	}
	CommandPollLoop();
}

/** Sends an ajax request
 *
 *
 **/
function Client_AjaxRequest(message) {
	logit("Client_AjaxRequest"
		+ "\n\t"+"MID:" + message.MID
		+ "\n\t"+"url:" + message.DATA.Arguments.url
		//+ "\n" + dump(message,"message")
	);
	/*
	 message: {} -->
	 message.Status: "OK"
	 message.SID: "4ab2091e63ba4e83ad60e983bd2d4acf"
	 message.MID: 1
	 message.DT: "command"
	 message.DATA: {} -->
	 message.DATA.Command: "ajaxrequest"
	 message.DATA.Arguments: {} -->
	 message.DATA.Arguments.url: "ajax/listReports.php"
	 message.DATA.Arguments.method: "post"
	 message.DATA.Arguments.parameters: {} -->
	 message.DATA.Arguments.parameters.pageNo: 0
	 message.DATA.Arguments.parameters.group: "a"
	 */

	var args = message.DATA.Arguments;
//	var m = "\n";
//	m += "g_ajaxpath:" + unsafeWindow.g_ajaxpath + "\n";
//	m += "g_ajaxsuffix:" + unsafeWindow.g_ajaxsuffix + "\n";
//	for (var n in unsafeWindow.g_ajaxparams) m += "g_ajaxparams." + n + ":" + unsafeWindow.g_ajaxparams[n]+"\n";
//	GM_log(m);

	if (typeof (args.method) == 'undefined' || args.method === null || args.method == '') args.method = "post";
	args.url = unsafeWindow.g_ajaxpath + args.url + unsafeWindow.g_ajaxsuffix; //TODO KoC specific??
	var parameters = unsafeWindow.Object.clone(unsafeWindow.g_ajaxparams); //TODO KoC specific??
	for (var n in args.parameters) parameters[n] = args.parameters[n];
	args.parameters = parameters;
	//GM_log("MyAjaxRequest.parameters\n"+dump(params, "*"));

	var req = new MyAjaxRequest(args.url, {
		method: args.method,
		parameters: args.parameters,
		onSuccess: function (rslt) {
			logit("Client_AjaxRequest MyAjaxRequest onSuccess"
				+ "\n\t" + "MID:" + message.MID
				+ "\n\t" + "url:" + message.DATA.Arguments.url
				+ "\n" + dump(rslt,"rslt")
			);
			SendCommandResponse(message, new TE.CommandRespData(true,null,rslt));
		},
		onFailure: function (rslt) {
			logit("Client_AjaxRequest MyAjaxRequest onFailure"
				+ "\n\t" + "MID:" + message.MID
				+ "\n\t" + "url:" + message.DATA.Arguments.url
				//+ "\n" + dump(rslt,"rslt")
			);
			SendCommandResponse(message, new CommandRespData(false, rslt, null));
		}
	}, false);

	CommandPollLoopCommandResp = { SID:message.SID, MID:message.MID, MT:"COMMAND_ACK", CH:0, DT:"", DATA:{} };
}

/** Client_eval
 *
 * @param {object} response
 */
function Client_eval(message) {
	try {
		var ret = eval(message.DATA.Arguments);
	} catch (ex) {
		var lna = ex.lineNumber; var me = ex.message; var na = ex.name; var ln;
		try {throw new Error("dummy"); } catch (ex2) { ln = lna - (ex2.lineNumber - 4); }
		// "ERROR: "+na+": "+me+" @line:"+ln;
		CommandPollLoopCommandResp = { SID:message.SID, MID:message.MID, MT: "COMMAND_RESP", DT: "CommandRespData", DATA: new TE.CommandRespData(false,{ name:na, message:me, lineNumber:ln}, null) };
		return;
	}
	CommandPollLoopCommandResp = { SID:message.SID, MID:message.MID, MT:"COMMAND_RESP", CH:0, DT: "CommandRespData", DATA:new TE.CommandRespData(true,null,ret) };
}

function EvalArguments(args) {
	try { return eval(args); }
	catch (ex) {
		var lna = ex.lineNumber; var me = ex.message; var na = ex.name; var ln;
		try {throw new Error("dummy"); } catch (ex2) { ln = lna - (ex2.lineNumber - 3); }
		return "ERROR: " + na + ": " + me + " @line:" + ln;
	}
}
function Reload() {
	// location.reload(true);
	// location.href=location.href;
	// window.location.href="http://apps.facebook.com/kingdomsofcamelot/?s=270";
	history.go(0);
}

function BeginPushKocData() {
	if (typeof (unsafeWindow.seed) == 'undefined') {
		if (Options.EnabledErrorConsoleTrace) logit("no seed");
	} else {
		setTimeout(function () { PushKocDataLoop(); }, 5);
	}
}

/** Send a response for a async command
 *
 * @param {object} request    then request message
 * @param {object} data       The data {CommandResponseData}
 **/
function SendCommandResponse(request, data) {
	if (Options.EnabledErrorConsoleTrace) logit("SendCommandResponse({MID:" + request.MID + "},..)");

	var myData = JSON.stringify({ SID: request.SID, MID: request.MID, MT: "COMMAND_RESP", CH:"U", DT: "", DATA: data });

	var ret = GM_xmlhttpRequest({
		method: "POST", url: ServerUri + "/command",
		headers: { "Content-Type": "application/json" },
		data: myData,
		onload: function (response) {
			if (Options.EnabledErrorConsoleTrace)
				logit("SendCommandResponse Success"
					+"\n"+response.responseText);
		},
		onerror: function (response) {
			if (Options.EnabledErrorConsoleTrace)
				logit("SendCommandResponse Failed"
					+ "\nlocation: " + window.location.href
					+ "\nresponseText: " + response.responseText
					+ "\nreadyState: " + response.readyState
					+ "\nresponseHeaders: " + response.responseHeaders
					+ "\nstatus: " + response.status
					+ "\nstatusText: " + response.statusText
					+ "\nfinalUrl: " + response.finalUrl);
			status("ERROR SendCommandResponse failed. Server offline?");
			//DISABLED setTimeout(function () { SendCommandResponse(messageId, status, data); }, 5000);
		}
	});
}

function PushKocDataLoop() {
	if (Options.EnabledErrorConsoleTrace) logit("PushKocDataLoop()");

	var kocData = {
		unsafeWindow: {
			seed: unsafeWindow.seed,
			cm: unsafeWindow.cm
		}
	};
	var myData = JSON.stringify({ SID: SessionId, MID: 0, MT: "data", DT: "unsafeWindow", DATA: kocData });

	var ret = GM_xmlhttpRequest({
		method: "POST", url: ServerUri + "/pushdata",
		headers: { "Content-Type": "application/json" },
		data: myData,
		onload: function (response) {
			if (Options.EnabledErrorConsoleTrace) logit(response.responseText);
			var command = response.responseJSON.Command;
			if (typeof (command) != "undefined") {
				switch (command) {
					case "alert": alert("huhu from " + ScriptName); break;
				}
			}
			setTimeout(function () { PushKocDataLoop(); }, 500);
		},
		onerror: function (response) {
			if (response.readyState == 4) {
				status("ERROR PushKocData failed. Server offline? Connecting...");
			} else {
				var msg = "An error occurred."
					+ "\nlocation: " + window.location.href
					+ "\nresponseText: " + response.responseText
					+ "\nreadyState: " + response.readyState
					+ "\nresponseHeaders: " + response.responseHeaders
					+ "\nstatus: " + response.status
					+ "\nstatusText: " + response.statusText
					+ "\nfinalUrl: " + response.finalUrl;
				logit(msg);
			}
			setTimeout(function () { InitConnection(); }, 5000);
		}
	});
}



function AjaxRequest (url, opts){
	var headers = {
		'X-Requested-With': 'XMLHttpRequest',
		'X-Prototype-Version': '1.6.1',
		'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
	};
	var ajax = null;

	if (window.XMLHttpRequest)
		ajax=new XMLHttpRequest();
	else
		ajax=new ActiveXObject("Microsoft.XMLHTTP");

	if (opts.method==null || opts.method=='')
		method = 'GET';
	else
		method = opts.method.toUpperCase();

	if (method == 'POST'){
		headers['Content-type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
	} else if (method == 'GET'){
		addUrlArgs (url, opts.parameters);
	}

	ajax.onreadystatechange = function() {
		if (ajax.readyState == 4) {
			if (ajax.status >= 200 && ajax.status < 305)
				if (opts.onSuccess) opts.onSuccess(ajax);
				else if (opts.onFailure) opts.onFailure(ajax);
		} else {
			if (opts.onChange) opts.onChange(ajax);
		}
	};
	ajax.open(method, url, true);
	for (var k1 in headers) ajax.setRequestHeader (k1, headers[k1]);
	if (matTypeof(opts.requestHeaders)=='object')
		for (var k2 in opts.requestHeaders)
			ajax.setRequestHeader (k2, opts.requestHeaders[k2]);

	if (method == 'POST'){
		var a = [];
		for (var k3 in opts.parameters){
			if(matTypeof(opts.parameters[k3]) == 'object')
				for(var h in opts.parameters[k3])
					a.push (k3+'['+h+'] ='+ opts.parameters[k3][h] );
			else
				a.push (k3 +'='+ opts.parameters[k3] );
		}
		ajax.send (a.join ('&'));
	} else               {
		ajax.send();
	}
}
function addUrlArgs (url, args){
	if (!args)
		return url;
	if (url.indexOf('?') < 0)
		url += '?';
	else if (url.substr(url.length-1) != '&')
		url += '&';
	if (matTypeof(args == 'object'))
		return url + implodeUrlArgs (args);
	return url + args;
}
function implodeUrlArgs (obj){
	var a = [];
	for (var k in obj)
		a.push (k +'='+ encodeURI(obj[k]) );
	return a.join ('&');
}
function matTypeof (v){
	if (v == undefined)
		return 'undefined';
	if (typeof (v) == 'object'){
		if (!v)
			return 'null';
		else if (v.constructor.toString().indexOf("Array")>=0 && typeof(v.splice)=='function')
			return 'array';
		else return 'object';
	}
	return typeof (v);
}
function MyAjaxRequest (url, o, noRetryX){
	var opts = unsafeWindow.Object.clone(o);
	var wasSuccess = o.onSuccess;
	var wasFailure = o.onFailure;
	var retry = 0;
	var delay = 3;
	var show=true;
	var silentTimer;
	var noRetry = noRetry===true?true:false;
	opts.onSuccess = mySuccess;
	opts.onFailure = myFailure;
	new AjaxRequest(url, opts);
	return;

	function myRetry(){
		++retry;
		if (retry>5) {
			//TODO
			// actionLog(""+culang.logSelectMoreError+"", "<font color=res>"+culang.errorQuery+"</font>");
			return false;
		}
		new AjaxRequest(url, opts);

		delay = delay * 1.20;
	}
	function myFailure(){
		var o = {};
		o.ok = false;
		o.errorMsg = "AJAX Communication Failure";
		wasFailure (o);
	}
	function mySuccess (msg){
		//logit(msg);
		var rslt;
		try {
			rslt = JSON.parse(msg.responseText);
		} catch(e) {

			if (retry<2) {
				rslt = { "ok": false, "error_code": 9, "errorMsg": "Failed due to invalid json" };
			} else {
				rslt = {"ok":true,"error_code":9,"data":[]};
			}

		}
		if (window.EmulateAjaxError){
			rslt.ok = false;
			rslt.error_code=8;
		}
		if (rslt.ok){
			if (rslt.updateSeed)
				unsafeWindow.update_seed(rslt.updateSeed);
			wasSuccess (rslt);
			return;
		}

		rslt.errorMsg = unsafeWindow.printLocalError((rslt.error_code || null), (rslt.msg || null), (rslt.feedback || null));

		if (!noRetry && (rslt.error_code==0 ||rslt.error_code==8 || rslt.error_code==1 || rslt.error_code==3)){
			// TODO
			// actionLog(lang.General.error, "<font color=res>"+ msg.responseText +"</font>");
			// dialogRetry (inspect(rslt.errorMsg), delay, function(){myRetry();}, function(){wasSuccess (rslt);}, rslt.error_code);
		} else if (!noRetry && rslt.error_code==9) {
			silentTimer = setTimeout(silentRetry, delay*100);
		} else {
			wasSuccess (rslt);
		}
	}
	function silentRetry() {
		clearTimeout(silentTimer);
		myRetry();
	}
}

/** Creates a recursive object dump
 *
 * @param {object} object the root object to dump.
 * @param {string} name of root object.
 * @return {string} string representation (dump) of object.
 */
function dump(object, rootName) {
	//GM_log("dump "+rootName);
	function dumpInternal(obj, path) {
		//GM_log("dumpInternal "+path);
		if (obj === null) {
			s += "" + path + ": {null}" + "\n";
			return;
		} else if (obj === undefined) {
			s += "" + path + ": {undefined}" + "\n";
			return;
		}
		var valueType = typeof (obj);
		switch (valueType) {
			case "function": return;
			case "object": s += "" + path + ": {} -->" /*+ propValue.toString().substr(0,20)*/ + "\n"; break;
			case "boolean": s += "" + path + ": " + obj.toString() + "\n"; return;
			case "number": s += "" + path + ": " + obj.toString() + "\n"; return;
			case "string": s += "" + path + ": \"" + obj.toString() + "\"\n"; return;
			default: s += "" + path + " (" + valueType + "): " + obj.toString() + "\n"; return;
		}

		for (var o in objs) {
			if (o === obj) {
				s += "{!Recursion stoped!}\n";
				return;
			} else objs.push(obj);
		}

		++ind;
		for (var propName in obj) dumpInternal(obj[propName], path + "." + propName);
		--ind;
	}
	var objs = [];
	var ind = 0;
	var s = "";
	if (typeof (rootName) == 'undefined') rootName = "*";
	dumpInternal(object, rootName);
	return s;
};
function logit2(msg) { //KoC specific
	var serverId = KOC.getServerId();
	var now = new Date();
	//	GM_log (serverID +' @ '+ now.toTimeString().substring (0,8) +'.' + now.getMilliseconds() +': '+  msg);
	GM_log('\n' + now.toTimeString().substring(0, 8) + '.' + now.getMilliseconds() + ' @ ' + serverId + ': ' + '\n' + msg);
}
function logit(msg) {
	var now = new Date();
	//	GM_log (serverID +' @ '+ now.toTimeString().substring (0,8) +'.' + now.getMilliseconds() +': '+  msg);
	GM_log('\n' + now.toTimeString().substring(0, 8) + '.' + now.getMilliseconds() + ': ' + '\n' + msg);
}

function status(msg) {
	var div = document.getElementById("LocalAppConnectorStatus");
	if (div == null) {
		div = document.createElement("div");
		div.setAttribute("id", "LocalAppConnectorStatus");
		div.setAttribute("style", "border-color: black; background-color: #f0e68c; margin-top: 2px; margin-left: 12px; margin-right: 1px; border-width: 1px; padding: 2px;")
		document.body.insertBefore(div, document.body.firstChild);
	}
	var now = new Date();
	var dateString = "[" + now.toTimeString().substring(0, 8) /* +'.' + now.getMilliseconds()*/ + "]";
	div.innerHTML = "<div><input id=LocalAppConnectorStatusHide type='submit' value='X' title='Hide this message'/> " + ScriptName + " " + dateString + " " + msg + "</div>";
	div.style.display = "block";
	document.getElementById("LocalAppConnectorStatusHide").addEventListener('click', function () {
		document.getElementById("LocalAppConnectorStatus").style.display = "none";
	}, false);
}

/** @namespace Provides Breconium specific stuff
 **/
var TE = {
	Message: function (messageDataObject) {
		/** Status
		 *
		 * @type {String}
		 */
		this.Status = "";			// Status			string		OK|ERR

		/** Session ID
		 *
		 * @type {String}
		 */
		this.SID    = SessionId;	// Session ID		string		"0" or GUID

		/** Message ID
		 *
		 * @type {Number}
		 */
		this.MID    = 0;			// Message ID		numeric		0-max

		/** Message type ( INIT | DATA | COMMAND_POLL | COMMAND_ACK | COMMAND_RESP )
		 *
		 * @type {String}
		 */
		this.MT     = "";			// Message Type		string

		this.CH     = null;

		/** Data Type
		 *
		 * @type {String}
		 */
		this.DT     = "";			// Data Type		string

		/** Data
		 *
		 * @type {*}
		 */
		this.DATA   = null;			// Data				object

		if(typeof(messageDataObject.Status)!='undefined') this.Status=messageDataObject.Status;
		if(typeof(messageDataObject.SID   )!='undefined') this.SID   =messageDataObject.SID   ;
		if(typeof(messageDataObject.MID   )!='undefined') this.MID   =messageDataObject.MID   ;
		if(typeof(messageDataObject.MT    )!='undefined') this.MT    =messageDataObject.MT    ;
		if(typeof(messageDataObject.CH    )!='undefined') this.CH    =messageDataObject.CH    ;
		if(typeof(messageDataObject.DT    )!='undefined') this.DT    =messageDataObject.DT    ;
		if(typeof(messageDataObject.DATA  )!='undefined') this.DATA  =messageDataObject.DATA  ;
	},

	/** Command Response Data
	 *
	 * @constructor
	 **/
	CommandRespData: function (success,error,retval) {
		this.Success     = success; // Success			bool		true | false
		this.Error       = error;
		this.ReturnValue = retval;
	},

	/** Command Request Data
	 *
	 * @constructor
	 **/
	CommandReqData:function(messageData) {
		this.Command   = "";
		this.Arguments = null;

		if(typeof(messageData.Command  )!='undefined') this.Command  =messageData.Command  ;
		if(typeof(messageData.Arguments)!='undefined') this.Arguments=messageData.Arguments;
	}
};

/** @namespace Provides KoC specific stuff
 **/
var KOC = {
	_serverId: null,
	getServerId: function () { if (_serverId == null) { var a = /^[a-zA-Z]+([0-9]+)\./.exec(document.location.hostname); if (a) _serverId = a[1]; else _serverId = "??"; } return _serverId; }

};

/* Scriptish API
 * https://github.com/scriptish/scriptish/wiki/Manual%3A-API
 **/
/* JSDoc
 * http://en.wikipedia.org/wiki/JSDoc
 * http://code.google.com/p/jsdoc-toolkit/w/list
 **/